#include "stdafx.h"
#include "CppUnitTest.h"
#include <vex/iterable/concept/range_concepts.h>
#include <vex/query/from.h>
#include <vex/iterable/make_any_range.h>
#include <vex.tests/mstest/tests.range/range_archetype.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests { namespace query_from {
    TEST_CLASS(test_class) {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.query.from")
        END_TEST_CLASS_ATTRIBUTE();

        VEX_TESTS_TRACKED_MEM_CHECK();
    public:
        TEST_METHOD_INITIALIZE(set_up)
        {
            VEX_TESTS_TRACKED_MEM_CHECK_START();
        }

        TEST_METHOD_CLEANUP(tear_down)
        {
            VEX_TESTS_TRACKED_MEM_CHECK_FINISH();
        }

        TEST_METHOD(from_delivers_an_iterable_range_for_container_like_types)
        {
            {
                typedef std::vector<int> container_t;
                typedef decltype(vex::query::from(std::declval<container_t&>())) result_t;
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::RandomAccessRange<result_t>));
                Assert::IsTrue(std::is_same<
                    decltype(vex::iterable::make_range(std::declval<container_t&>())),
                    result_t
                >::value);
            }

            {
                typedef std::list<int> container_t;
                typedef decltype(vex::query::from(std::declval<container_t&>())) result_t;
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<result_t>));
                Assert::IsTrue(std::is_same<
                    decltype(vex::iterable::make_range(std::declval<container_t&>())),
                    result_t
                >::value);
            }

            {
                typedef std::map<int, std::string> container_t;
                typedef decltype(vex::query::from(std::declval<container_t&>())) result_t;
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<result_t>));
                Assert::IsTrue(std::is_same<
                    decltype(vex::iterable::make_range(std::declval<container_t&>())),
                    result_t
                >::value);
            }
        }

        TEST_METHOD(from_delivers_an_iterable_range_for_container_like_types_boost_1_55_msc_1800)
        {
#if _MSC_VER == 1800 && BOOST_VERSION == 105500
#pragma message(__FILE__ " boost::container::slist does not compile with boost 105500 and MSC 1800")
            Assert::Fail(wlog_message()
                << __FILE__
                << L" boost::container::slist does not compile with boost 105500 and MSC 1800"
                | as_string);
#else
            {
                typedef boost::container::slist<int> container_t;
                typedef decltype(vex::query::from(std::declval<container_t&>())) result_t;
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<result_t>));
                Assert::IsTrue(std::is_same<
                    decltype(vex::iterable::make_range(std::declval<container_t&>())), 
                    result_t
                >::value); 
            }
#endif
        }

        TEST_METHOD(from_delivers_an_iterable_range_for_piped_boost_ranges)
        {
            {
                typedef std::vector<int> container_t;
                auto p = [](int v) { return v % 2 == 0; };
                typedef decltype(vex::query::from(
                    std::declval<container_t&>() | boost::adaptors::filtered(p))) result_t;
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<result_t>));
                Assert::IsTrue(std::is_same<
                    decltype(vex::iterable::make_range(std::declval<container_t&>() | boost::adaptors::filtered(p))),
                    result_t
                >::value);
            }

            {
                typedef std::list<int> container_t;
                auto p = [](int v) { return v % 2 == 0; };
                typedef decltype(vex::query::from(
                    std::declval<container_t&>() | boost::adaptors::filtered(p))) result_t;
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<result_t>));
                Assert::IsTrue(std::is_same<
                    decltype(vex::iterable::make_range(std::declval<container_t&>() | boost::adaptors::filtered(p))),
                    result_t
                >::value);
            }

            {
                typedef std::map<int, std::string> container_t;
                auto p = [](int v) { return v % 2 == 0; };
                typedef decltype(vex::query::from(
                    std::declval<container_t&>()
                    | boost::adaptors::map_keys
                    | boost::adaptors::filtered(p))) result_t;
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::BiDirectionalRange<result_t>));
                Assert::IsTrue(std::is_same<
                    decltype(vex::iterable::make_range(
                    std::declval<container_t&>()
                    | boost::adaptors::map_keys
                    | boost::adaptors::filtered(p))),
                    result_t
                >::value);
            }
        }

        TEST_METHOD(from_delivers_an_iterable_range_for_piped_boost_ranges_boost_1_55_msc_1800)
        {
#if _MSC_VER == 1800 && BOOST_VERSION == 105500
#pragma message(__FILE__ " boost::container::slist does not compile with boost 105500 and MSC 1800")
            Assert::Fail(wlog_message()
                << __FILE__
                << L" boost::container::slist does not compile with boost 105500 and MSC 1800"
                | as_string);
#else
            {
                typedef boost::container::slist<int> container_t;
                auto p = [](int v) { return v % 2 == 0; };
                typedef decltype(vex::query::from(
                    std::declval<container_t&>()
                    | boost::adaptors::filtered(p))) result_t;
                BOOST_CONCEPT_ASSERT((vex::iterable::concept::ForwardRange<result_t>));
                Assert::IsTrue(std::is_same<
                    decltype(vex::iterable::make_range(std::declval<container_t&>() | boost::adaptors::filtered(p))), 
                    result_t
                >::value); 
            }
#endif
        }

        TEST_METHOD(from_is_unit_transformation_for_ranges)
        {
            {
                typedef std::vector<int> container_t;
                typedef decltype(vex::iterable::make_range(std::declval<container_t&>())) in_t;
                typedef decltype(vex::query::from(std::declval<in_t>())) out_t;

                Assert::IsTrue(std::is_same<in_t&&, out_t&&>::value, wlog_message()
                    << "expected: " << std::endl
                    << typeid(in_t).name() << std::endl
                    << "got: " << std::endl
                    << typeid(out_t).name() << std::endl | as_string);

                Assert::IsTrue(std::is_same<in_t&, out_t&>::value);

                // why is this failing?
                //Assert::IsTrue(std::is_same<in_t const&, out_t const&>::value);
                // this behaves as expected:
                {
                    container_t t_container;
                    auto r_in = vex::iterable::make_range(t_container);
                    auto & r_out = vex::query::from(r_in);
                    Assert::IsTrue(&r_out == &r_in);
                }

                {
                    container_t t_container;
                    const auto t_in = vex::iterable::make_range(t_container);
                    auto const & t_out = vex::query::from(t_in);

                    Assert::IsTrue(&t_out == &t_in);
                }

                {
                    container_t t_container;
                    auto r_in = vex::iterable::make_range(t_container.begin(), t_container.end());
                    auto & r_out = vex::query::from(r_in);
                    Assert::IsTrue(&r_out == &r_in);
                }

                {
                    container_t t_container;
                    const auto t_in = vex::iterable::make_range(t_container.cbegin(), t_container.cend());
                    auto const & t_out = vex::query::from(t_in);

                    Assert::IsTrue(&t_out == &t_in);
                }
            }

            {
                typedef std::list<int> container_t;
                typedef decltype(vex::iterable::make_range(std::declval<container_t&>())) in_t;
                typedef decltype(vex::query::from(std::declval<in_t>())) out_t;

                Assert::IsTrue(std::is_same<in_t&&, out_t&&>::value, wlog_message()
                    << "expected: " << std::endl
                    << typeid(in_t).name() << std::endl
                    << "got: " << std::endl
                    << typeid(out_t).name() << std::endl | as_string);
            }

            {
                typedef std::map<int, std::string> container_t;
                typedef decltype(vex::iterable::make_range(std::declval<container_t&>())) in_t;
                typedef decltype(vex::query::from(std::declval<in_t>())) out_t;

                Assert::IsTrue(std::is_same<in_t&&, out_t&&>::value, wlog_message()
                    << "expected: " << std::endl
                    << typeid(in_t).name() << std::endl
                    << "got: " << std::endl
                    << typeid(out_t).name() << std::endl | as_string);
            }
        }

        TEST_METHOD(from_is_unit_transformation_for_ranges_boost_1_55_msc_1800)
        {
#if _MSC_VER == 1800 && BOOST_VERSION == 105500
#pragma message(__FILE__ " boost::container::slist does not compile with boost 105500 and MSC 1800")
            Assert::Fail(wlog_message()
                << __FILE__
                << L" boost::container::slist does not compile with boost 105500 and MSC 1800"
                | as_string);
#else
            {
                typedef boost::container::slist<int> container_t;
                typedef decltype(vex::iterable::make_range(std::declval<container_t&>())) in_t;
                typedef decltype(vex::query::from(std::declval<in_t>())) out_t;

                Assert::IsTrue(std::is_same<in_t&&, out_t&&>::value, wlog_message() 
                    << "expected: " << std::endl
                    << typeid(in_t).name() << std::endl
                    << "got: " << std::endl
                    << typeid(out_t).name() << std::endl | as_string); 
            }
#endif
        }

        TEST_METHOD(from_is_unit_transformation_for_any_range)
        {
            {
                typedef std::vector<int> container_t;
                typedef decltype(vex::iterable::make_any_range(std::declval<container_t&>())) in_t;
                typedef decltype(vex::query::from(std::declval<in_t&&>())) out_t;

                Assert::IsTrue(std::is_same<in_t&&, out_t&&>::value, wlog_message()
                    << "expected: " << std::endl
                    << typeid(in_t).name() << std::endl
                    << "got: " << std::endl
                    << typeid(out_t).name() << std::endl | as_string);

                {
                    container_t t_container;
                    auto r_in = vex::iterable::make_any_range(t_container);
                    auto & r_out = vex::query::from(r_in);
                    Assert::IsTrue(&r_out == &r_in);
                }

                {
                    container_t t_container;
                    const auto t_in = vex::iterable::make_any_range(t_container);
                    auto const & t_out = vex::query::from(t_in);

                    Assert::IsTrue(&t_out == &t_in);
                }

                {
                    container_t t_container;
                    auto r_in = vex::iterable::make_any_range(t_container.begin(), t_container.end());
                    auto & r_out = vex::query::from(r_in);
                    Assert::IsTrue(&r_out == &r_in);
                }

                {
                    container_t t_container;
                    const auto t_in = vex::iterable::make_any_range(t_container.cbegin(), t_container.cend());
                    auto const & t_out = vex::query::from(t_in);

                    Assert::IsTrue(&t_out == &t_in);
                }
            }

            {
                typedef std::list<int> container_t;
                typedef decltype(vex::iterable::make_any_range(std::declval<container_t&>())) in_t;
                typedef decltype(vex::query::from(std::declval<in_t>())) out_t;

                Assert::IsTrue(std::is_same<in_t&&, out_t&&>::value, wlog_message()
                    << "expected: " << std::endl
                    << typeid(in_t).name() << std::endl
                    << "got: " << std::endl
                    << typeid(out_t).name() << std::endl | as_string);

                {
                    container_t t_container;
                    auto r_in = vex::iterable::make_any_range(t_container);
                    auto & r_out = vex::query::from(r_in);
                    Assert::IsTrue(&r_out == &r_in);
                }

                {
                    container_t t_container;
                    const auto t_in = vex::iterable::make_any_range(t_container);
                    auto const & t_out = vex::query::from(t_in);

                    Assert::IsTrue(&t_out == &t_in);
                }

                {
                    container_t t_container;
                    auto r_in = vex::iterable::make_any_range(t_container.begin(), t_container.end());
                    auto & r_out = vex::query::from(r_in);
                    Assert::IsTrue(&r_out == &r_in);
                }

                {
                    container_t t_container;
                    const auto t_in = vex::iterable::make_any_range(t_container.cbegin(), t_container.cend());
                    auto const & t_out = vex::query::from(t_in);

                    Assert::IsTrue(&t_out == &t_in);
                }
            }

            {
                typedef std::map<int, std::string> container_t;
                typedef decltype(vex::iterable::make_any_range(std::declval<container_t&>())) in_t;
                typedef decltype(vex::query::from(std::declval<in_t>())) out_t;

                Assert::IsTrue(std::is_same<in_t&&, out_t&&>::value, wlog_message()
                    << "expected: " << std::endl
                    << typeid(in_t).name() << std::endl
                    << "got: " << std::endl
                    << typeid(out_t).name() << std::endl | as_string);

                {
                    container_t t_container;
                    auto r_in = vex::iterable::make_any_range(t_container);
                    auto & r_out = vex::query::from(r_in);
                    Assert::IsTrue(&r_out == &r_in);
                }

                {
                    container_t t_container;
                    const auto t_in = vex::iterable::make_any_range(t_container);
                    auto const & t_out = vex::query::from(t_in);

                    Assert::IsTrue(&t_out == &t_in);
                }

                {
                    container_t t_container;
                    auto r_in = vex::iterable::make_any_range(t_container.begin(), t_container.end());
                    auto & r_out = vex::query::from(r_in);
                    Assert::IsTrue(&r_out == &r_in);
                }

                {
                    container_t t_container;
                    const auto t_in = vex::iterable::make_any_range(t_container.cbegin(), t_container.cend());
                    auto const & t_out = vex::query::from(t_in);

                    Assert::IsTrue(&t_out == &t_in);
                }
            }
        }

        TEST_METHOD(from_is_unit_transformation_for_any_range_boost_boost_1_55_msc_1800)
        {
#if _MSC_VER == 1800 && BOOST_VERSION == 105500
#pragma message(__FILE__ " boost::container::slist does not compile with boost 105500 and MSC 1800")
            Assert::Fail(wlog_message()
                << __FILE__
                << L" boost::container::slist does not compile with boost 105500 and MSC 1800"
                | as_string);
#else
            {
                typedef boost::container::slist<int> container_t;
                typedef decltype(vex::iterable::make_any_range(std::declval<container_t&>())) in_t;
                typedef decltype(vex::query::from(std::declval<in_t>())) out_t;

                Assert::IsTrue(std::is_same<in_t&&, out_t&&>::value, wlog_message() 
                    << "expected: " << std::endl
                    << typeid(in_t).name() << std::endl
                    << "got: " << std::endl
                    << typeid(out_t).name() << std::endl | as_string); 

                {
                    container_t t_container;
                    auto r_in = vex::iterable::make_any_range(t_container);
                    auto & r_out = vex::query::from(r_in);
                    Assert::IsTrue(&r_out == &r_in);
                }

                {
                    container_t t_container;
                    const auto t_in = vex::iterable::make_any_range(t_container);
                    auto const & t_out = vex::query::from(t_in);

                    Assert::IsTrue(&t_out == &t_in);
                }

                {
                    container_t t_container;
                    auto r_in = vex::iterable::make_any_range(t_container.begin(), t_container.end());
                    auto & r_out = vex::query::from(r_in);
                    Assert::IsTrue(&r_out == &r_in);
                }

                {
                    container_t t_container;
                    const auto t_in = vex::iterable::make_any_range(t_container.cbegin(), t_container.cend());
                    auto const & t_out = vex::query::from(t_in);

                    Assert::IsTrue(&t_out == &t_in);
                }
            }
#endif
        }

        TEST_METHOD(from_forwards_ranges_and_creates_range_from_stl_elements)
        {
            typedef std::vector<int> container_t;
            auto t_predicate = [](int v) { return v % 2 == 0; };

            {
                typedef decltype(vex::query::from(std::declval<container_t&>())) from_result_t;
                Assert::IsTrue(std::is_same<
                    decltype(vex::iterable::make_range(std::declval<container_t&>())),
                    from_result_t
                >::value);
            }

            {
                typedef decltype(vex::query::from(std::declval<container_t const&>())) from_result_t;
                Assert::IsTrue(std::is_same<
                    decltype(vex::iterable::make_range(std::declval<container_t const&>())),
                    from_result_t
                >::value);
            }

            {
                typedef tests::range_archetype<vex::iterable::random_access, int>&& range_t;
                typedef decltype(vex::query::from(std::declval<range_t>())) from_result_t;

                Assert::IsTrue(std::is_same<
                    range_t,
                    from_result_t&&
                >::value);
            }

            {
                typedef tests::range_archetype<vex::iterable::random_access, int>& range_t;
                typedef decltype(vex::query::from(std::declval<range_t>())) from_result_t;

                Assert::IsTrue(std::is_same<
                    range_t,
                    from_result_t
                >::value);
            }

            {
                typedef tests::range_archetype<vex::iterable::random_access, int> const& range_t;
                typedef decltype(vex::query::from(std::declval<range_t>())) from_result_t;

                Assert::IsTrue(std::is_same<
                    range_t,
                    from_result_t
                >::value);
            }
        }

    };
}}